#ifndef __LS_SINK_H__
#define __LS_SINK_H__

#include "util.hpp"
#include <memory>
#include <fstream>
#include <sstream>
#include <cassert>
#include <cstring>



namespace log_system
{
    class LogSink
    {
    public:
        using ptr = std::shared_ptr<LogSink>;
        virtual ~LogSink(){}
        virtual void log(const char* data, size_t len) = 0;
    };

    class StdoutSink : public LogSink
    {
    public:
        void log(const char* data, size_t len) override
        {
            std::cout.write(data, len);
        }
    };

    class FileSink : public LogSink
    {
    public:
        FileSink(const std::string& fileName):
            _fileName(fileName)
        {
            util::File::createDirectory(util::File::parentPath(_fileName));
            _ofs.open(_fileName, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
        }

        

        void log(const char* data, size_t len) override
        {
            _ofs.write(data, len);
            assert(_ofs.good());
        }
    private:
        std::string _fileName;
        std::ofstream _ofs;
    };

    class RollingBySizeSink : public LogSink
    {
    public:
        RollingBySizeSink(const std::string& baseName, size_t maxSize)
            :_baseName(baseName),
            _maxSize(maxSize),
            _curSize(0),
            _name_count(0)
        {
            util::File::createDirectory(util::File::parentPath(_baseName));
            createNewFile();
        }
        void log(const char* data, size_t len) override
        {
            if(_curSize > _maxSize)
            {
                createNewFile();
            }
            //不需要太精细，一条日志内容不会太多(而且这样即使maxSize为0也能一个文件写入一条日志)
            _ofs.write(data, len);
            _curSize += len;
            assert(_ofs.good());
        }
    private:
        void createNewFile()
        {
            if(_ofs.is_open())  _ofs.close();
            
            std::string fileName = getFileName();
            _ofs.open(fileName, std::ios::binary | std::ios::app);
            assert(_ofs.is_open());
            _curSize = 0;
        }
        std::string getFileName()
        {
            std::stringstream ss;
            ss << _baseName << _name_count++ << ".log";

            return ss.str();
        }

    private:
        std::string _baseName;
        std::ofstream _ofs;
        size_t _maxSize;
        size_t _curSize;
        size_t _name_count;
    };


    class SinkFactory
    {
    public:
        template<typename SinkType, typename ...Args>
        static LogSink::ptr create(Args&& ...args)
        {
            return std::make_shared<SinkType>(std::forward<Args>(args)...);
        }
    };
}


#endif